/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-02-20     chenbin      add
 */

#include <board.h>
#include "drv_spi.h"

#ifdef BSP_USING_SPI

//#define DRV_DEBUG
#define LOG_TAG             "drv.spi"
#include <drv_log.h>

#include "n32g45x.h"
#include "n32g45x_spi.h"

struct n32_spi_bus
{
  SPI_InitType        SPI_InitStructure;
	SPI_Module*         Instance;
	IRQn_Type           irq_type;
	char*               bus_name;
	struct rt_spi_configuration*  cfg;
	struct rt_spi_bus             spi_bus;
};

struct n32_spi_device_data
{
    rt_uint32_t pin;
    const char *bus_name;
    const char *device_name;
};


enum
{
#ifdef BSP_USING_SPI1
    SPI1_INDEX,
#endif
#ifdef BSP_USING_SPI2
    SPI2_INDEX,
#endif
#ifdef BSP_USING_SPI3
    SPI3_INDEX,
#endif
#ifdef BSP_USING_SPI4
    SPI4_INDEX,
#endif
#ifdef BSP_USING_SPI5
    SPI5_INDEX,
#endif
#ifdef BSP_USING_SPI6
    SPI6_INDEX,
#endif
};

static struct n32_spi_bus  n32_spi_bus_obj[] = 
{
#ifdef BSP_USING_SPI1
    {
			.bus_name = "spi1",
			.Instance = SPI1,
			.irq_type = SPI1_IRQn,
    },
#endif
#ifdef BSP_USING_SPI2
    {
			.bus_name = "spi2",
			.Instance = SPI2,
			.irq_type = SPI2_IRQn,
    },
#endif
#ifdef BSP_USING_SPI3
    {
			.bus_name = "spi3",
			.Instance = SPI3,
			.irq_type = SPI3_IRQn,
    },
#endif
};

#if defined(BSP_USING_SPI1)
void SPI1_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif

#if defined(BSP_USING_SPI2) 
void SPI2_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();


    /* leave interrupt */
    rt_interrupt_leave();
}
#endif

extern void n32_msp_spi_init(void *Instance);

static rt_err_t n32_spi_init(struct n32_spi_bus *spi_bus, struct rt_spi_configuration *cfg)
{
    RT_ASSERT(spi_bus != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);
	
	  SPI_InitStruct(&spi_bus->SPI_InitStructure);
	
		n32_msp_spi_init(spi_bus->Instance);
		
    if (cfg->mode & RT_SPI_SLAVE)
    {
        spi_bus->SPI_InitStructure.SpiMode = SPI_MODE_SLAVE;
    }
    else
    {
        spi_bus->SPI_InitStructure.SpiMode = SPI_MODE_MASTER;
    }

    if (cfg->mode & RT_SPI_3WIRE)
    {
        spi_bus->SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX | SPI_DIR_SINGLELINE_RX;
    }
    else
    {
        spi_bus->SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    }

    if (cfg->data_width == 8)
    {
			spi_bus->SPI_InitStructure.DataLen = SPI_DATA_SIZE_8BITS;
    }
    else if (cfg->data_width == 16)
    {
			spi_bus->SPI_InitStructure.DataLen = SPI_DATA_SIZE_16BITS;
    }
    else
    {
        return RT_EIO;
    }

    if (cfg->mode & RT_SPI_CPHA)
    {
			spi_bus->SPI_InitStructure.CLKPHA = SPI_CLKPHA_SECOND_EDGE;
    }
    else
    {
			spi_bus->SPI_InitStructure.CLKPHA = SPI_CLKPHA_FIRST_EDGE;
    }

    if (cfg->mode & RT_SPI_CPOL)
    {
			spi_bus->SPI_InitStructure.CLKPOL = SPI_CLKPOL_HIGH;
    }
    else
    {
			spi_bus->SPI_InitStructure.CLKPOL = SPI_CLKPOL_LOW;
    }

    if (cfg->mode & RT_SPI_NO_CS)
    {
			spi_bus->SPI_InitStructure.NSS = SPI_NSS_HARD;
    }
    else
    {
			spi_bus->SPI_InitStructure.NSS = SPI_NSS_SOFT;
    }
		
		int div = 0;
		RCC_ClocksType RCC_Clocks = {0};
		RCC_GetClocksFreqValue(&RCC_Clocks);
		
    if (cfg->max_hz >= RCC_Clocks.Pclk2Freq / 2)
    {
			div = 2;
			spi_bus->SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_2;
    }
    else if (cfg->max_hz >= RCC_Clocks.Pclk2Freq / 4)
    {
			div = 4;
			spi_bus->SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_4;
    }
    else if (cfg->max_hz >= RCC_Clocks.Pclk2Freq / 8)
    {
			div = 8;
			spi_bus->SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_8;
    }
    else if (cfg->max_hz >= RCC_Clocks.Pclk2Freq / 16)
    {
			div = 16;
			spi_bus->SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_16;
    }
    else if (cfg->max_hz >= RCC_Clocks.Pclk2Freq / 32)
    {
			div = 32;
			spi_bus->SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_32;
    }
    else if (cfg->max_hz >= RCC_Clocks.Pclk2Freq / 64)
    {
			div = 64;
			spi_bus->SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_64;
    }
    else if (cfg->max_hz >= RCC_Clocks.Pclk2Freq / 128)
    {
			div = 128;
			spi_bus->SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_128;
    }
    else
    {
			div = 256;
			spi_bus->SPI_InitStructure.BaudRatePres = SPI_BR_PRESCALER_256;
    }

    LOG_I("sys freq: %d, pclk2 freq: %d, SPI limiting freq: %d, div: %d",
					RCC_Clocks.SysclkFreq ,
          RCC_Clocks.Pclk2Freq ,
          cfg->max_hz ,
          div);

    if (cfg->mode & RT_SPI_MSB)
    {
			spi_bus->SPI_InitStructure.FirstBit = SPI_FB_MSB;
    }
    else
    {
			spi_bus->SPI_InitStructure.FirstBit = SPI_FB_LSB;
    }
		spi_bus->SPI_InitStructure.CRCPoly = 0x07;
		
		SPI_Enable(spi_bus->Instance, DISABLE);  //disable
		
		SPI_EnableCalculateCrc(spi_bus->Instance, DISABLE);   //disable crc
		
		SPI_Init(spi_bus->Instance, &spi_bus->SPI_InitStructure);  //init
		
		SPI_Enable(spi_bus->Instance, ENABLE);   //enable
		
    LOG_D("%s init done", spi_bus->bus_name);
    return RT_EOK;
}


static rt_err_t n32_spi_configure(struct rt_spi_device *device,struct rt_spi_configuration *configuration)
{
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(configuration != RT_NULL);

    struct n32_spi_bus *spi_bus =  rt_container_of(device->bus, struct n32_spi_bus, spi_bus);
    spi_bus->cfg = configuration;

    return n32_spi_init(spi_bus, configuration);
}

static int N32_SPI_TXRX(SPI_Module* Instance,uint8_t *tx_data,uint8_t *rx_data,uint16_t tx_len)
{
	uint16_t index = 0;
	while(index < tx_len)
	{
		/*!< Loop while DAT register in not emplty */
    while (SPI_I2S_GetStatus(Instance, SPI_I2S_TE_FLAG) == RESET);

    /*!< Send byte through the SPI1 peripheral */
    SPI_I2S_TransmitData(Instance, tx_data[index]);

    /*!< Wait to receive a byte */
    while (SPI_I2S_GetStatus(Instance, SPI_I2S_RNE_FLAG) == RESET);
		
    /*!< Return the byte read from the SPI bus */
    rx_data[index] =  SPI_I2S_ReceiveData(Instance);
		
		index++;
	}
	return 0;
}

static int N32_SPI_TX(SPI_Module* Instance,uint8_t *tx_data,uint16_t tx_len)
{
	uint16_t temp = 0;
	uint16_t index = 0;
	while(index < tx_len)
	{
		/*!< Loop while DAT register in not emplty */
    while (SPI_I2S_GetStatus(Instance, SPI_I2S_TE_FLAG) == RESET);

    /*!< Send byte through the SPI1 peripheral */
    SPI_I2S_TransmitData(Instance, tx_data[index]);

    /*!< Wait to receive a byte */
    while (SPI_I2S_GetStatus(Instance, SPI_I2S_RNE_FLAG) == RESET);
		
    /*!< Return the byte read from the SPI bus */
    temp = SPI_I2S_ReceiveData(Instance);
		index++;
	}
	return 0;
}

static int N32_SPI_RX(SPI_Module* Instance,uint8_t *rx_data,uint16_t tx_len)
{
	uint16_t index = 0;
	while(index < tx_len)
	{
		/*!< Loop while DAT register in not emplty */
    while (SPI_I2S_GetStatus(Instance, SPI_I2S_TE_FLAG) == RESET);

    /*!< Send byte through the SPI1 peripheral */
    SPI_I2S_TransmitData(Instance, 0xFF);

    /*!< Wait to receive a byte */
    while (SPI_I2S_GetStatus(Instance, SPI_I2S_RNE_FLAG) == RESET);
		
    /*!< Return the byte read from the SPI bus */
    rx_data[index] = SPI_I2S_ReceiveData(Instance);
		
		index++;
	}
	return 0;
}


static rt_uint32_t n32_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
    rt_size_t message_length, already_send_length;
    rt_uint16_t send_length;
    rt_uint8_t *recv_buf;
    const rt_uint8_t *send_buf;

    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(device->bus != RT_NULL);
    //RT_ASSERT(device->bus->parent.user_data != RT_NULL);
	  RT_ASSERT(device->parent.user_data != RT_NULL);
    RT_ASSERT(message != RT_NULL);

    struct n32_spi_bus *spi_bus =  rt_container_of(device->bus, struct n32_spi_bus, spi_bus);
	
    struct n32_spi_device_data *spi_device_data = device->parent.user_data;

    if (message->cs_take && !(device->config.mode & RT_SPI_NO_CS))
    {
        if (device->config.mode & RT_SPI_CS_HIGH)
				{
					rt_pin_write(spi_device_data->pin, PIN_HIGH);
				}
        else
				{
					rt_pin_write(spi_device_data->pin, PIN_LOW);
				}
    }

    LOG_D("%s transfer prepare and start", spi_bus->bus_name);
    LOG_D("%s sendbuf: %X, recvbuf: %X, length: %d",
          spi_bus->bus_name,
          (uint32_t)message->send_buf,
          (uint32_t)message->recv_buf, message->length);

    message_length = message->length;
    recv_buf = message->recv_buf;
    send_buf = message->send_buf;
    while (message_length)
    {
        /* the HAL library use uint16 to save the data length */
        if (message_length > 65535)
        {
            send_length = 65535;
            message_length = message_length - 65535;
        }
        else
        {
            send_length = message_length;
            message_length = 0;
        }

        /* calculate the start address */
        already_send_length = message->length - send_length - message_length;
        send_buf = (rt_uint8_t *)message->send_buf + already_send_length;
        recv_buf = (rt_uint8_t *)message->recv_buf + already_send_length;

        /* start once data exchange in DMA mode */
        if (message->send_buf && message->recv_buf)
        {
					N32_SPI_TXRX(spi_bus->Instance, (uint8_t *)send_buf, (uint8_t *)recv_buf, send_length);
        }
        else if (message->send_buf)
        {
					N32_SPI_TX(spi_bus->Instance, (uint8_t *)send_buf, send_length);
        }
        else
        {
					memset((uint8_t *)recv_buf, 0xff, send_length);
					N32_SPI_RX(spi_bus->Instance, (uint8_t *)recv_buf, send_length);
        }
    }

    if (message->cs_release && !(device->config.mode & RT_SPI_NO_CS))
    {
        if (device->config.mode & RT_SPI_CS_HIGH)
				{
					rt_pin_write(spi_device_data->pin, PIN_LOW );
				}
        else
				{
					rt_pin_write(spi_device_data->pin, PIN_HIGH );
				}
    }
    return message->length;
}

static const struct rt_spi_ops n32_spi_ops =
{
    .configure = n32_spi_configure,
    .xfer = n32_spi_xfer,
};

static int rt_hw_spi_bus_init(void)
{
    rt_err_t result;
	  int len = sizeof(n32_spi_bus_obj) / sizeof(n32_spi_bus_obj[0]);
    for (int i = 0; i < len; i++)
    {
			result = rt_spi_bus_register(&n32_spi_bus_obj[i].spi_bus, n32_spi_bus_obj[i].bus_name, &n32_spi_ops);
			RT_ASSERT(result == RT_EOK);

			LOG_D("%s bus init done", n32_spi_bus_obj[i].bus_name);
    }
    return result;
}

int rt_hw_spi_init(void)
{
    return rt_hw_spi_bus_init();
}
INIT_BOARD_EXPORT(rt_hw_spi_init);

/**
  * Attach the spi device to SPI bus, this function must be used after initialization.
  */
rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, uint32_t cs_gpio_pin)
{
    RT_ASSERT(bus_name != RT_NULL);
    RT_ASSERT(device_name != RT_NULL);

    rt_err_t result;
    struct rt_spi_device *spi_device;
    struct n32_spi_device_data *spi_device_data;

    /* initialize the cs pin && select the slave*/
		rt_pin_mode(cs_gpio_pin, PIN_MODE_OUTPUT);
		rt_pin_write(cs_gpio_pin, PIN_HIGH );
		
    /* attach the device to spi bus*/
    spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
    RT_ASSERT(spi_device != RT_NULL);
    spi_device_data = (struct n32_spi_device_data *)rt_malloc(sizeof(struct n32_spi_device_data));
    RT_ASSERT(spi_device_data != RT_NULL);
    spi_device_data->pin = cs_gpio_pin;
		spi_device_data->bus_name = bus_name;
		spi_device_data->device_name = device_name;
		
    result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)spi_device_data);

    if (result != RT_EOK)
    {
        LOG_E("%s attach to %s faild, %d\n", device_name, bus_name, result);
    }

    RT_ASSERT(result == RT_EOK);

    LOG_D("%s attach to %s done", device_name, bus_name);

    return result;
}


#endif /* RT_USING_SPI */

